home *** CD-ROM | disk | FTP | other *** search
/ Internet Info 1994 March / Internet Info CD-ROM (Walnut Creek) (March 1994).iso / networking / ip / ka9q / src.arc / KERNEL.C < prev    next >
C/C++ Source or Header  |  1989-08-19  |  12KB  |  481 lines

  1. /* Non pre-empting synchronization kernel, machine-independent portion */
  2. #if    defined(PROCLOG) || defined(PROCTRACE)
  3. #include <stdio.h>
  4. #endif
  5. #include <dos.h>
  6. #include <setjmp.h>
  7. #include "global.h"
  8. #include "mbuf.h"
  9. #include "proc.h"
  10. #include "timer.h"
  11. #include "socket.h"
  12. #include "pc.h"
  13.  
  14. #ifdef    PROCLOG
  15. FILE *proclog;
  16. FILE *proctrace;
  17. #endif
  18. int Stkchk = 0;
  19. struct proc *Curproc;        /* Currently running process */
  20. struct proc *Rdytab;        /* Processes ready to run (not including curproc) */
  21. struct proc *Waittab[PHASH];    /* Waiting process list */
  22. struct proc *Susptab;        /* Suspended processes */
  23. static struct mbuf *Killq;
  24.  
  25. static void addproc __ARGS((struct proc *entry));
  26. static void delproc __ARGS((struct proc *entry));
  27. static unsigned phash __ARGS((void *event));
  28.  
  29. /* Create a process descriptor for the main function. Must be actually
  30.  * called from the main function!
  31.  */
  32. struct proc *
  33. mainproc(name)
  34. char *name;
  35. {
  36.     register struct proc *pp;
  37.  
  38.     /* Create process descriptor */
  39.     if((pp = (struct proc *)calloc(1,sizeof(struct proc))) == NULLPROC)
  40.         return NULLPROC;
  41.  
  42.     /* Create name */
  43.     pp->name = strdup(name);
  44.     pp->stksize = 65535;
  45.     psetup(pp,0,NULL,NULL,NULLVFP);
  46.  
  47.     /* Make current */
  48.     pp->state = READY;
  49.     Curproc = pp;
  50.  
  51. #ifdef    PROCLOG
  52.     proclog = fopen("proclog",APPEND_TEXT);
  53.     proctrace = fopen("proctrace",APPEND_TEXT);
  54. #endif
  55.     return pp;
  56. }
  57. /* Create a new, ready process and return pointer to descriptor.
  58.  * The general registers are not initialized, but optional args are pushed
  59.  * on the stack so they can be seen by a C function.
  60.  */
  61. struct proc *
  62. newproc(name,stksize,pc,iarg,parg1,parg2)
  63. char *name;        /* Arbitrary user-assigned name string */
  64. unsigned int stksize;    /* Stack size in words to allocate */
  65. void (*pc)();        /* Initial execution address */
  66. int iarg;        /* Integer argument (argc) */
  67. void *parg1;        /* Generic pointer argument #1 (argv) */
  68. void *parg2;        /* Generic pointer argument #2 (session ptr) */
  69. {
  70.     register struct proc *pp;
  71.     int i;
  72.  
  73.     if(Stkchk)
  74.         chkstk();
  75.  
  76.     /* Create process descriptor */
  77.     if((pp = (struct proc *)calloc(1,sizeof(struct proc))) == NULLPROC)
  78.         return NULLPROC;
  79.  
  80.     /* Create name */
  81.     pp->name = strdup(name);
  82.  
  83.     /* Allocate stack */
  84.     pp->stksize = stksize;
  85.     if((pp->stack = (int16 *)malloc(sizeof(int16)*stksize)) == NULL){
  86.         free(pp->name);
  87.         free((char *)pp);
  88.         return NULLPROC;
  89.     }
  90.     /* Initialize stack for high-water check */
  91.     for(i=0;i<stksize;i++)
  92.         pp->stack[i] = STACKPAT;
  93.  
  94.     /* Do machine-dependent initialization of stack */
  95.     psetup(pp,iarg,parg1,parg2,pc);
  96.  
  97.     /* Add to ready process table */
  98.     pp->state = READY;
  99.     addproc(pp);
  100.     return pp;
  101. }
  102.  
  103. /* Free resources allocated to specified process. If a process wants to kill
  104.  * itself, the reaper is called to do the dirty work. This avoids some
  105.  * messy situations that would otherwise occur, like freeing your own stack.
  106.  */
  107. void
  108. killproc(pp)
  109. register struct proc *pp;
  110. {
  111.     if(pp == NULLPROC)
  112.         return;
  113.     /* Don't check the stack here! Will cause infinite recursion if
  114.      * called from a stack error
  115.      */
  116.  
  117.     if(pp == Curproc)
  118.         killself();    /* Doesn't return */
  119.  
  120.     /* Close any open sockets */
  121.     freesock(pp);
  122.  
  123.     /* Stop alarm clock in case it's running */
  124.     stop_timer(&pp->alarm);
  125.  
  126.     /* Alert everyone waiting for this proc to die */
  127.     psignal(pp,0);
  128.  
  129.     /* Remove from appropriate table */
  130.     delproc(pp);
  131.  
  132. #ifdef    PROCLOG
  133.     fprintf(proclog,"id %lx name %s stack %u/%u\n",ptol(pp),
  134.         pp->name,stkutil(pp),pp->stksize);
  135.     fclose(proclog);
  136.     proclog = fopen("proclog",APPEND_TEXT);
  137.     proctrace = fopen("proctrace",APPEND_TEXT);
  138. #endif
  139.     /* Free allocated memory resources */
  140.     free(pp->name);
  141.     free(pp->stack);
  142.     free((char *)pp);
  143. }
  144. /* Terminate current process by sending a request to the killer process.
  145.  * Automatically called when a process function returns. Does not return.
  146.  */
  147. void
  148. killself()
  149. {
  150.     register struct mbuf *bp;
  151.  
  152.     if(Curproc != NULLPROC){
  153.         bp = pushdown(NULLBUF,sizeof(Curproc));
  154.         memcpy(bp->data,(char *)&Curproc,sizeof(Curproc));
  155.         enqueue(&Killq,bp);
  156.     }
  157.     /* "Wait for me; I will be merciful and quick." */
  158.     for(;;)
  159.         pwait(NULL);
  160. }
  161. /* Process used by processes that want to kill themselves */
  162. void
  163. killer(i,v1,v2)
  164. int i;
  165. void *v1;
  166. void *v2;
  167. {
  168.     struct proc *pp;
  169.     struct mbuf *bp;
  170.  
  171.     for(;;){
  172.         while(Killq == NULLBUF)
  173.             pwait(&Killq);
  174.         bp = dequeue(&Killq);
  175.         pullup(&bp,(char *)&pp,sizeof(pp));
  176.         free_p(bp);
  177.         if(pp != Curproc)    /* We're immortal */
  178.             killproc(pp);
  179.     }                        
  180. }
  181.  
  182. #ifdef    notused
  183. /* Inhibit a process from running */
  184. static void
  185. suspend(pp)
  186. struct proc *pp;
  187. {
  188.     if(pp == NULLPROC)
  189.         return;
  190.     if(pp != Curproc)
  191.         delproc(pp);    /* Running process isn't on any list */
  192.     pp->state |= SUSPEND;
  193.     if(pp != Curproc)
  194.         addproc(pp);    /* pwait will do it for us */
  195.     else
  196.         pwait(NULL);
  197. }
  198. /* Restart suspended process */
  199. static void
  200. resume(pp)
  201. struct proc *pp;
  202. {
  203.     if(pp == NULLPROC)
  204.         return;
  205.     delproc(pp);    /* Can't be Curproc! */
  206.     pp->state &= ~SUSPEND;
  207.     addproc(pp);
  208. }
  209. #endif    /* notused */
  210.  
  211. /* Wakeup waiting process, regardless of event it's waiting for. The process
  212.  * will see a return value of "val" from its pwait() call.
  213.  */
  214. void
  215. alert(pp,val)
  216. struct proc *pp;
  217. int val;
  218. {
  219.     if(pp == NULLPROC)
  220.         return;
  221.     if((pp->state & WAITING) == 0)
  222.         return;
  223. #ifdef    PROCTRACE
  224.     printf("alert(%lx,%u) [%s]\n",ptol(pp),val,pp->name);
  225.     fflush(stdout);
  226. #endif
  227.     if(pp != Curproc)
  228.         delproc(pp);
  229.     pp->state &= ~WAITING;
  230.     pp->retval = val;
  231.     pp->event = 0;
  232.     addproc(pp);
  233. }
  234.  
  235. /* Post a wait on a specified event and give up the CPU until it happens. The
  236.  * null event is special: it means "I don't want to block on an event, but let
  237.  * somebody else run for a while". It can also mean that the present process
  238.  * is terminating; in this case the wait never returns.
  239.  *
  240.  * Pwait() returns 0 if the event was signaled; otherwise it returns the
  241.  * arg in an alert() call. Pwait must not be called from interrupt level.
  242.  */
  243. int
  244. pwait(event)
  245. void *event;
  246. {
  247.     struct proc *oldproc;
  248.     char i_state;
  249.     int tmp;
  250.  
  251.     i_state = dirps();
  252.     if(Curproc != NULLPROC){    /* If process isn't terminating */
  253.         if(Stkchk)
  254.             chkstk();
  255.  
  256.         if(event == NULL){
  257.             /* Special case; just give up the processor.
  258.              *
  259.              * Optimization: if nothing else is ready, just return.
  260.              */
  261.             if(Rdytab == NULLPROC){
  262.                 restore(i_state);
  263.                 return 0;
  264.             }
  265.         } else {
  266.             /* Post a wait for the specified event */
  267.             Curproc->event = event;
  268.             Curproc->state = WAITING;
  269.         }
  270.         addproc(Curproc);
  271.     }
  272.     /* Look for a ready process and run it. If there are none,
  273.      * loop or halt until an interrupt makes something ready.
  274.      */
  275.     while(Rdytab == NULLPROC){
  276.         /* give system back to upper-level multitasker, if any */
  277.         giveup();
  278.     }
  279.     /* Remove first entry from ready list */
  280.     oldproc = Curproc;
  281.     Curproc = Rdytab;
  282.     delproc(Curproc);
  283.  
  284.     /* Now do the context switch.
  285.      * This technique was inspired by Rob, PE1CHL, and is a bit tricky.
  286.      *
  287.      * If the old process has gone away, simply load the new process's
  288.      * environment. Otherwise, save the current process's state. Then if
  289.      * this is still the old process, load the new environment. Since the
  290.      * new task will "think" it's returning from the setjmp() with a return
  291.      * value of 1, the comparison with 0 will bypass the longjmp(), which
  292.      * would otherwise cause an infinite loop.
  293.      */
  294. #ifdef    PROCTRACE
  295.     if(strcmp(oldproc->name,Curproc->name) != 0){
  296.           printf("-> %s(%d)\n",Curproc->name,Curproc->i_state);
  297.         fflush(stdout);
  298.     }
  299. #endif
  300.     /* Note use of comma operator to save old interrupt state only if
  301.      * oldproc is non-null
  302.      */
  303.     if(oldproc == NULLPROC
  304.      || (oldproc->i_state = i_state, setjmp(oldproc->env) == 0)){
  305.         /* We're still running in the old task; load new task context.
  306.          * The interrupt state is restored here in case longjmp
  307.          * doesn't do it (e.g., systems other than Turbo-C).
  308.          */
  309.         restore(Curproc->i_state);
  310.         longjmp(Curproc->env,1);
  311.     }
  312.     /* At this point, we're running in the newly dispatched task */
  313.     tmp = Curproc->retval;
  314.     Curproc->retval = 0;
  315.  
  316.     /* Also restore the true interrupt state here, in case the longjmp
  317.      * DOES restore the interrupt state saved at the time of the setjmp().
  318.      * This is the case with Turbo-C's setjmp/longjmp.
  319.      */
  320.     restore(Curproc->i_state);
  321.     return tmp;
  322. }
  323.  
  324. /* Make ready the first 'n' processes waiting for a given event. The ready
  325.  * processes will see a return value of 0 from pwait().  Note that they don't
  326.  * actually get control until we explicitly give up the CPU ourselves through
  327.  * a pwait(). Psignal may be called from interrupt level.
  328.  */
  329. void
  330. psignal(event,n)
  331. void *event;    /* Event to signal */
  332. int n;        /* Max number of processes to wake up */
  333. {
  334.     register struct proc *pp;
  335.     struct proc *pnext;
  336.     int i_state;
  337.  
  338.     if(Stkchk)
  339.         chkstk();
  340.  
  341.     if(event == NULL)
  342.         return;        /* Null events are invalid */
  343.  
  344.     /* n = 0 means "signal everybody waiting for this event" */
  345.     if(n == 0)
  346.         n = 65535;
  347.  
  348.     i_state = dirps();
  349.     for(pp = Waittab[phash(event)];n != 0 && pp != NULLPROC;pp = pnext){
  350.         pnext = pp->next;
  351.         if(pp->event == event){
  352. #ifdef    PROCTRACE
  353.             if(i_state){
  354.                 printf("psignal(%lx,%u) wake %lx [%s]\n",ptol(event),n,
  355.                  ptol(pp),pp->name);
  356.                 fflush(stdout);
  357.             }
  358. #endif
  359.             delproc(pp);
  360.             pp->state &= ~WAITING;
  361.             pp->event = 0;
  362.             pp->retval = 0;
  363.             n--;
  364.             addproc(pp);
  365.         }
  366.     }
  367.     for(pp = Susptab;n != 0 && pp != NULLPROC;pp = pnext){
  368.         pnext = pp->next;
  369.         if(pp->event == event){
  370. #ifdef    PROCTRACE
  371.             if(i_state){
  372.                 printf("psignal(%lx,%u) wake %lx [%s]\n",ptol(event),n,
  373.                  ptol(pp),pp->name);
  374.                 fflush(stdout);
  375.             }
  376. #endif
  377.             delproc(pp);
  378.             pp->state &= ~WAITING;
  379.             pp->event = 0;
  380.             pp->retval = 0;
  381.             addproc(pp);
  382.             n--;
  383.         }
  384.     }
  385.     restore(i_state);
  386. }
  387.  
  388. /* Rename a process */
  389. void
  390. chname(pp,newname)
  391. struct proc *pp;
  392. char *newname;
  393. {
  394.     free(pp->name);
  395.     pp->name = strdup(newname);
  396. }
  397. /* Remove a process entry from the appropriate table */
  398. static void
  399. delproc(entry)
  400. register struct proc *entry;    /* Pointer to entry */
  401. {
  402.     int i_state;
  403.  
  404.     if(entry == NULLPROC)
  405.         return;
  406.  
  407.     i_state = dirps();
  408.     if(entry->next != NULLPROC)
  409.         entry->next->prev = entry->prev;
  410.     if(entry->prev != NULLPROC)
  411.         entry->prev->next = entry->next;
  412.     else {
  413.         switch(entry->state){
  414.         case READY:
  415.             Rdytab = entry->next;
  416.             break;
  417.         case WAITING:
  418.             Waittab[phash(entry->event)] = entry->next;
  419.             break;
  420.         case SUSPEND:
  421.         case SUSPEND|WAITING:
  422.             Susptab = entry->next;
  423.             break;
  424.         }
  425.     }
  426.     restore(i_state);
  427. }
  428. /* Append proc entry to end of appropriate list */
  429. static void
  430. addproc(entry)
  431. register struct proc *entry;    /* Pointer to entry */
  432. {
  433.     register struct proc *pp;
  434.     struct proc **head;
  435.     int i_state;
  436.  
  437.     if(entry == NULLPROC)
  438.         return;
  439.  
  440.     i_state = dirps();
  441.     switch(entry->state){
  442.     case READY:
  443.         head = &Rdytab;
  444.         break;
  445.     case WAITING:
  446.         head = &Waittab[phash(entry->event)];
  447.         break;
  448.     case SUSPEND:
  449.     case SUSPEND|WAITING:
  450.         head = &Susptab;
  451.         break;
  452.     }
  453.     entry->next = NULLPROC;
  454.     if(*head == NULLPROC){
  455.         /* Empty list, stick at beginning */
  456.         entry->prev = NULLPROC;
  457.         *head = entry;
  458.     } else {
  459.         /* Find last entry on list */
  460.         for(pp = *head;pp->next != NULLPROC;pp = pp->next)
  461.             ;
  462.         pp->next = entry;
  463.         entry->prev = pp;
  464.     }
  465.     restore(i_state);
  466. }
  467. static unsigned
  468. phash(event)
  469. void *event;
  470. {
  471.     register char *cp = (char *)&event;
  472.     int i = sizeof(event);
  473.     register unsigned x = 0;
  474.  
  475.     while(i-- != 0)
  476.         x ^= *cp++;
  477.  
  478.     return x % PHASH;
  479. }
  480.  
  481.